#include "SSLContext.hpp"
#include <openssl/err.h>
#include <muduo/base/Logging.h>


using namespace ssl;

SSLContext::SSLContext(const SSLConfig &config):
    _config(config),
    _ctx(nullptr)
{
}

SSLContext::~SSLContext()
{
    if(_ctx)
    {
        SSL_CTX_free(_ctx);//释放上下文
    }
}

bool SSLContext::init()
{
    //初始化OpenSSL
    OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS|OPENSSL_INIT_LOAD_CRYPTO_STRINGS, nullptr);
    //OPENSSL_INIT_LOAD_SSL_STRINGS加载SSL字符串，OPENSSL_INIT_LOAD_CRYPTO_STRINGS加载加密算法字符串

    const SSL_METHOD *method = TLS_server_method();//获取TLS服务器方法
    _ctx = SSL_CTX_new(method);//创建上下文
    if(!_ctx)
    {
        handleSSLError("Failed to create SSL context");
        return false;
    }

    //设置SSL选项
    long options = SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_COMPRESSION | SSL_OP_CIPHER_SERVER_PREFERENCE;//禁用SSLv2、SSLv3、压缩和服务器优先选择密码
    SSL_CTX_set_options(_ctx,options);//设置上下文选项

    //加载证书文件
    if(!loadCertFile())
    {
        return false;
    }
    //设置协议
    if(!setupProtocol())
    {
        return false;
    }
    //设置会话缓存
    setupSessionCache();
    LOG_INFO<<"SSLContext init success";
    return true;
}

bool SSLContext::loadCertFile()
{
    //加载证书文件
    if(SSL_CTX_use_certificate_file(_ctx,_config.getCertFile().c_str(),SSL_FILETYPE_PEM) <= 0)
    {
        handleSSLError("Failed to load certificate file");
        return false;
    }
    //加载私钥文件
    if(SSL_CTX_use_PrivateKey_file(_ctx,_config.getPrivateKeyFile().c_str(),SSL_FILETYPE_PEM) <= 0)
    {
        handleSSLError("Failed to load private key file");
        return false;
    }

    //验证私钥与证书是否匹配
    if(!SSL_CTX_check_private_key(_ctx))
    {
        handleSSLError("Private key does not match the certificate");
        return false;
    }

    //加载链文件
    if(!_config.getChainFile().empty())
    {
        //如果链文件存在，加载链文件
        if(SSL_CTX_use_certificate_chain_file(_ctx,_config.getChainFile().c_str()) <= 0)
        {
            handleSSLError("Failed to load chain file");
            return false;
        }
    }

    return true;
}
// 设置协议 反向控制逻辑：通过禁用特定版本来实现版本选择 而不是直接启用某个版本 保证了版本选择的灵活性、允许客户端和服务端根据自己的需求选择最佳的协议版本、便于安全策略的精细控制
bool SSLContext::setupProtocol()
{
    // 设置 SSL/TLS 协议版本
    long options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;//默认禁用已知不安全的协议版本
    switch (_config.getProtocolVersion())
    {
        case SSLVersion::TLS_1_0:
            options |= SSL_OP_NO_TLSv1; // 禁用 TLS 1.0
            break;
        case SSLVersion::TLS_1_1:
            options |= SSL_OP_NO_TLSv1_1; // 禁用 TLS 1.1
            break;
        case SSLVersion::TLS_1_2:
            options |= SSL_OP_NO_TLSv1_2; // 禁用 TLS 1.2
            break;
        case SSLVersion::TLS_1_3:
            options |= SSL_OP_NO_TLSv1_3; // 禁用 TLS 1.3
            break;
    }
    SSL_CTX_set_options(_ctx, options);
    
    // 设置加密套件
    if (!_config.getCipherList().empty())
    {
        if (SSL_CTX_set_cipher_list(_ctx,
            _config.getCipherList().c_str()) <= 0)
        {
            handleSSLError("Failed to set cipher list");
            return false;
        }
    }

    return true;
}   

void SSLContext::setupSessionCache()
{
    // 设置会话缓存
    SSL_CTX_set_session_cache_mode(_ctx, SSL_SESS_CACHE_SERVER);//设置会话缓存模式为服务器模式
    SSL_CTX_sess_set_cache_size(_ctx, _config.getSessionCacheSize());//设置会话缓存大小
    SSL_CTX_set_timeout(_ctx, _config.getSessionTimeout());//设置会话超时时间
}

void SSLContext::handleSSLError(const char *err)
{
    char errorBuffer[256];
    ERR_error_string_n(ERR_get_error(), errorBuffer, sizeof(errorBuffer));//获取错误字符串
    LOG_ERROR << "OpenSSL error: " << err << " (" << errorBuffer << ")";
}